#include "Args.h"
#include <windows.h> /* GlobalFree */

#include <sstream>
#include <memory>
using namespace std;

#define DEFAULT_LENGTH 2000
#define CAST(pos, type) static_cast<type*>(pos)
#define SHIFT(pos, offset) static_cast<void*>(static_cast<char*>(pos) + offset)

Args::Args()
	: m_len(0), m_data(nullptr), m_ext(nullptr), m_wos(nullptr), m_ros(nullptr)
{
	m_data = malloc(DEFAULT_LENGTH);
	m_len = DEFAULT_LENGTH;

	m_wos = SHIFT(m_data, 9); // 1 byte for result, 8 bytes for pointer to extended data
	m_ros = m_data;
}
Args::~Args()
{
	if(m_data)
	{
		free(m_data);
		m_data = nullptr;
	}
	if(m_ext)
	{
		GlobalFree(m_ext);
		m_ext = nullptr;
	}
}
wstring Args::toString() const
{
	wstringstream ss;
	ss << m_data;
	return ss.str();
}
Args& Args::extend()
{
	get(m_ext);
	m_ros = m_ext;
	return *this;
}
void Args::ensureCapacity(size_t increment)
{
	size_t offset = reinterpret_cast<size_t>(m_wos) - reinterpret_cast<size_t>(m_data);

	if(offset + increment > m_len)
	{
		size_t newsize = (m_len + increment) * 2;
		m_data = realloc(m_data, newsize);
		m_len = newsize;

		m_wos = SHIFT(m_data, offset);
		m_ros = m_data;
	}
}
#define IMPL_ARGS_PUT(type) \
	Args& Args::put(type v) \
{\
	size_t increment = sizeof(type);\
	ensureCapacity(increment);\
	*CAST(m_wos, type) = v;\
	m_wos = SHIFT(m_wos, increment);\
	return *this;\
}
IMPL_ARGS_PUT(void*);
IMPL_ARGS_PUT(bool);
IMPL_ARGS_PUT(char);
IMPL_ARGS_PUT(unsigned char);
IMPL_ARGS_PUT(short);
IMPL_ARGS_PUT(unsigned short);
IMPL_ARGS_PUT(int);
IMPL_ARGS_PUT(unsigned int);
IMPL_ARGS_PUT(float);
IMPL_ARGS_PUT(double);
IMPL_ARGS_PUT(long long);
IMPL_ARGS_PUT(unsigned long long);
Args& Args::put(const wstring& value)
{
	int symbol = sizeof(unsigned short);
	int size = static_cast<unsigned short>(value.size()) * sizeof(wchar_t);
	int increment = size + symbol;
	ensureCapacity(increment);

	// first word -> size of the string 
	//	if the string is larger than 65535, the data will be truncated
	put(static_cast<unsigned short>(value.size()));

	// copy string
	memcpy(m_wos, value.data(), size);
	m_wos = SHIFT(m_wos, size);
	return *this;
}
#define IMPL_ARGS_GET(type) \
	Args& Args::get(type& v) \
{\
	size_t increment = sizeof(type);\
	v = *CAST(m_ros, type);\
	m_ros = SHIFT(m_ros, increment);\
	return *this;\
}
IMPL_ARGS_GET(void*);
IMPL_ARGS_GET(bool);
IMPL_ARGS_GET(char);
IMPL_ARGS_GET(unsigned char);
IMPL_ARGS_GET(short);
IMPL_ARGS_GET(unsigned short);
IMPL_ARGS_GET(int);
IMPL_ARGS_GET(unsigned int);
IMPL_ARGS_GET(float);
IMPL_ARGS_GET(double);
IMPL_ARGS_GET(long long);
IMPL_ARGS_GET(unsigned long long);
Args& Args::get(wstring& value)
{
	// get string length
	unsigned short size;
	get(size);

	// copy the data
	value = wstring(CAST(m_ros, wchar_t), size);
	m_ros = SHIFT(m_ros, size * sizeof(wchar_t));
	return *this;
}